#pragma once
#include <iostream>
#include "mutex.hpp"
#include <filesystem>
#include <sstream>
#include <fstream>
#include <unistd.h>
#include <cstdio>
#include <string>
#include <time.h>
namespace logger_module
{
    using namespace mutex_module;
   
    #define grep "\r\n"
    class log_strategy
    {
    public:
        log_strategy(){}
        ~log_strategy() = default;
        virtual void sync_log(std::string mes)=0;
    private:
    };
    //显示器刷新策略
    class console_log_strategy:public log_strategy
    {
    public:
        console_log_strategy(){}
        void sync_log(std::string mes) override
        {
            _mutex.lock();
            std::cout<<mes<<grep;
            _mutex.unlock();
        }
    private:
        mutex _mutex;
    };
    const std::string default_path ="./";
    const std::string default_src  ="my.log";
    //文件刷新策略
    class file_log_strategy:public log_strategy
    {
    public:
        file_log_strategy(const std::string& path=default_path,const std::string&src=default_src)
        :_pathname(path),
        _srcname(src)
        {
            _mutex.lock();
            std::filesystem::create_directories(_pathname);
            _mutex.unlock();
        }
        void sync_log(std::string mes) override
        {
            _mutex.lock();
            std::string filename=_pathname+(_pathname.back()=='/'? "":"/")+_srcname;
            std::ofstream out(filename,std::ios::app);
            out<<mes<<grep;
            out.close();
            _mutex.unlock();
        }
    private:
        mutex _mutex;
        std::string _pathname;
        std::string _srcname;
    };
    enum class log_level
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };
    std::string level_to_string(log_level l)
    {
        switch (l)
        {
            case log_level::DEBUG:
                return "DEBUG";
            case log_level::INFO:
                return "INFO";
            case log_level::WARNING:
                return "WARNING";
            case log_level::ERROR:
                return "ERROR";
            case log_level::FATAL:
                return "FATAL";
            default:
                return "UNKNOWN";
        }
    }
    std::string get_time_stamp()
    {
        time_t cur=time(nullptr);
        struct tm curr_tm;
        localtime_r(&cur,&curr_tm);
        char timebuffer[128];
        snprintf(timebuffer,sizeof(timebuffer),"%4d-%02d-%02d %02d:%02d:%02d",curr_tm.tm_year+1900,
            curr_tm.tm_mon+1,
            curr_tm.tm_mday,
            curr_tm.tm_hour,
            curr_tm.tm_min,
            curr_tm.tm_sec);
        return timebuffer;
    }
    class logger
    {
    public:
        logger()
        {
            pls=std::make_unique<console_log_strategy>();
        }
        void switch_to_console()
        {
            pls=std::make_unique<console_log_strategy>();
        }
        void switch_to_file()
        {
            pls=std::make_unique<file_log_strategy>();
        }
        class log_message
        {
        public:
            log_message(log_level level,std::string src,int line,logger& log)
            :_time(get_time_stamp()),
            _level(level),
            _pid(getpid()),
            _src(src),
            _line(line),
            _log(log)
            {
                std::stringstream ss;
                ss << "[" << _time << "] "
                   << "[" << level_to_string(_level) <<"] "
                   << "[" << _pid <<"] "
                   << "[" << _src <<"] "
                   << "[" << _line <<"] "
                   <<"- ";
                _message=ss.str();
            }
        template<class T>
        log_message& operator<<(const T& mes)
        {
            std::stringstream ss;
            ss<<mes;
            _message+=ss.str();
            return *this;
        }
        ~log_message()
        {
            _log.pls->sync_log(_message);
        }
        private:
            std::string _time;
            log_level _level;
            pid_t _pid;
            std::string _src;
            int _line;
            std::string _message;
            logger& _log;
        };
    private:
        std::unique_ptr<log_strategy> pls;
    public:
        log_message operator()(log_level level,std::string src,int line)
        {
            return log_message(level,src,line,*this);
        }
    };
    logger log;
    #define LOG(level) log(level,__FILE__,__LINE__)
    #define switch_console_log_strategy() log.switch_to_console()
    #define switch_file_log_strategy()  log.switch_to_file()
}
